﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;

namespace proj01
{
    public class Frog
    {
        #region Fields

        private Game1 game;
        private Model model;
        private int leftFrontLeg;
        private int rightFrontLeg;
        private int leftBackLeg;
        private int rightBackLeg;
        private int head;
        private float leftBackLegAngle = 0;
        private float leftFrontLegAngle = 0;
        private float rightBackLegAngle = 0;
        private float rightFrontLegAngle = 0;
        private float headLiftAngle = 0;
        private float headTurnAngle = 0;
        private bool jump = false;
        private Vector3 position = Vector3.Zero;
        private Vector3 velocity = Vector3.Zero;
        private Vector3 acceleration = Vector3.Zero;
        private float azimuth = 0;
        private float elevation = 0;
        private float roll = 0;
        private float headLiftRate = 0;
        private float headTurnRate = 0;
        private Vector3 gravity = new Vector3(0, -700, 0);
        private float turnRate = 0;
        private const float MaxTurnRate = (float)Math.PI;
        private bool legsGoBack = false;
        private int laserNext = 0;
        private Vector3[] laserLocs = { new Vector3(-50, 250, 300), new Vector3(50, 250, 300) };
        private LaserFire laserFire;


        #endregion

        #region Properties

        public bool Jump { get { return jump; } set { jump = value; } }
        public float HeadTurnRate { get { return headTurnRate; } set { headTurnRate = value; } }
        public float HeadLiftRate { get { return headLiftRate; } set { headLiftRate = value; } }
        public Vector3 Position { get { return position; } set { position = value; } }
        public Vector3 Velocity { get { return velocity; } set { velocity = value; } }
        public Vector3 Acceleration { get { return acceleration; } set { acceleration = value; } }
        public float TurnRate { get { return turnRate; } set { turnRate = value; } }
        public LaserFire LaserFire { get { return laserFire; } }

        public Matrix Transform
        {
            get
            {
                return Matrix.CreateRotationZ(-roll) *
                        Matrix.CreateRotationX(elevation) *
                        Matrix.CreateRotationY(azimuth) *
                        Matrix.CreateTranslation(position);
            }
        }

        public Matrix LaserTransform
        {
            get
            {
                return Matrix.CreateRotationZ(-roll) *
                        Matrix.CreateRotationX(headLiftAngle) *
                        Matrix.CreateRotationY(azimuth + headTurnAngle) *
                        Matrix.CreateTranslation(position);
            }
        }

        #endregion

        #region Constructor and Load

        /// <summary>
        /// Constructor of the frog
        /// </summary>
        /// <param name="froggame"></param>
        public Frog(Game1 froggame)
        {
            this.game = froggame;
            laserFire = new LaserFire(game);
        }

        /// <summary>
        /// This function is called to load content into this component
        /// of our game.
        /// </summary>
        /// <param name="content">The content manager to load from.</param>
        public void LoadContent(ContentManager content)
        {
            model = content.Load<Model>("Frog-rigid");
            leftFrontLeg = model.Bones.IndexOf(model.Bones["LeftFrontLeg"]);
            rightFrontLeg = model.Bones.IndexOf(model.Bones["RightFrontLeg"]);
            leftBackLeg = model.Bones.IndexOf(model.Bones["LeftBackLeg"]);
            rightBackLeg = model.Bones.IndexOf(model.Bones["RightBackLeg"]);
            head = model.Bones.IndexOf(model.Bones["Head"]);
            laserFire.LoadContent(content);
        }

        #endregion

        #region Update

        /// <summary>
        /// This function is called to update this component of our game
        /// to the current game time.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            float backLegDeployTime = 0.05f;     // Seconds
            game.SoundBank.PlayCue("46241__esformouse__frog");

            // Rotate the frog's legs for jumping
            if (jump && leftBackLegAngle < 0.5f)
            {
                leftBackLegAngle += (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / backLegDeployTime);
                rightBackLegAngle += (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / backLegDeployTime);
                leftFrontLegAngle += (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / backLegDeployTime);
                rightFrontLegAngle += (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / backLegDeployTime);
                if (leftBackLegAngle >= 0.5f)
                    legsGoBack = true;
            }

            if (legsGoBack && leftBackLegAngle > 0)
            {
                leftBackLegAngle -= (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / backLegDeployTime);
                rightBackLegAngle -= (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / backLegDeployTime);
                leftFrontLegAngle -= (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / backLegDeployTime);
                rightFrontLegAngle -= (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / backLegDeployTime);
                if (leftBackLegAngle <= 0)
                    legsGoBack = false;
            }

            // Get the frog to jump
            if (velocity != Vector3.Zero)
            {
                Vector3 drag = Vector3.Zero - 0.1f * velocity;
                acceleration += gravity + drag;
                velocity += acceleration * (float)gameTime.ElapsedGameTime.TotalSeconds;
                position += velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;

                if (position.Y <= 0)
                {
                    acceleration.X = 0;
                    velocity.X = 0;

                    position.Y = 0;
                    velocity.Y = 0;
                    acceleration.Y = 0;
                    jump = false;

                    velocity.Z = 0;
                    acceleration.Z = 0;
                }
            }
            float headDeployTime = 0.2f;    // Seconds

            // Rotate the frog's head up and down
            if (headLiftRate == -1)
            {
                headLiftAngle -= (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / headDeployTime);
                if (headLiftAngle <= -0.8f)
                    headLiftAngle = -0.8f;
            }
            else if (headLiftRate == 1)
            {
                headLiftAngle += (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / headDeployTime);
                if (headLiftAngle >= 0.8f)
                    headLiftAngle = 0.8f;
            }

            // Rotate the frog's head left and right
            if (headTurnRate == -1)
            {
                headTurnAngle -= (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / headDeployTime);
                if (headTurnAngle <= -0.9f)
                    headTurnAngle = -0.9f;
            }
            else if (headTurnRate == 1)
            {
                headTurnAngle += (float)(0.20 * gameTime.ElapsedGameTime.TotalSeconds / headDeployTime);
                if (headTurnAngle >= 0.9f)
                    headTurnAngle = 0.9f;
            }

            // Get the frog to turn
            azimuth += turnRate * MaxTurnRate * (float)gameTime.ElapsedGameTime.TotalSeconds;
            Matrix transform = Matrix.CreateRotationX(elevation) *
                Matrix.CreateRotationY(azimuth);

            laserFire.Update(gameTime);
        }

        #endregion

        #region Draw

        /// <summary>
        /// This function is called to draw this game component.
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTime"></param>
        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            Matrix transform = Matrix.CreateRotationZ(roll) * Matrix.CreateRotationX(elevation) *
                Matrix.CreateRotationY(azimuth) *
                Matrix.CreateTranslation(position);

            DrawModel(graphics, model, transform);

            laserFire.Draw(graphics, gameTime);
        }

        private void DrawModel(GraphicsDeviceManager graphics, Model model, Matrix world)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);
            transforms[leftBackLeg] = Matrix.CreateRotationX(leftBackLegAngle) * transforms[leftBackLeg];
            transforms[rightBackLeg] = Matrix.CreateRotationX(rightBackLegAngle) * transforms[rightBackLeg];
            transforms[leftFrontLeg] = Matrix.CreateRotationX(leftFrontLegAngle) * transforms[leftFrontLeg];
            transforms[rightFrontLeg] = Matrix.CreateRotationX(rightFrontLegAngle) * transforms[rightFrontLeg];
            transforms[head] = Matrix.CreateRotationZ(headTurnAngle) * transforms[head];
            transforms[head] = Matrix.CreateRotationX(headLiftAngle) * transforms[head];

            //Scales the Model up
            Matrix worldMatrix = Matrix.CreateScale(50f, 50f, 50f) * world;

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index] * worldMatrix;

                    effect.View = game.Camera.View;
                    effect.Projection = game.Camera.Projection;
                }
                mesh.Draw();
            }
        }

        #endregion

        #region lasers

        public void FireLaser()
        {
            game.SoundBank.PlayCue("tx0_fire1");
            Vector3 position = Vector3.Transform(laserLocs[laserNext], LaserTransform);
            Matrix orientation = LaserTransform;
            orientation.Translation = Vector3.Zero;

            Vector3 direction = Vector3.TransformNormal(new Vector3(0, 0, 1), orientation);

            laserNext = (laserNext + 1) % 2;

            float speed = velocity.Length();

            laserFire.FireLaser(position, orientation, speed);
        }
        #endregion
    }
}
